using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
using Net.SamuelChen.Libs.WinForm;

namespace Net.SamuelChen.Libs.WinForm.Sys {
	#region Enum MouseLLHookAction
	// MouseLL hook actions
	public enum MouseLLHookAction : int {
		WM_MOUSEMOVE = 0x200,
		WM_LBUTTONDOWN = 0x201, 
		WM_LBUTTONUP = 0x202, 
		WM_LBUTTONDBLCLK = 0x203,
		WM_RBUTTONDOWN = 0x204,
		WM_RBUTTONUP = 0x205,
		WM_RBUTTONDBLCLK = 0x206,
		WM_MOUSEWHEEL = 0x20A, 
		WM_MOUSEHOVER = 0x2A1,
		WM_MOUSELEAVE = 0x2A3,
	}
	#endregion

	#region struct MSLLHOOKSTRUCT
	[StructLayout(LayoutKind.Sequential)]
	public struct MSLLHOOKSTRUCT {
		Point pt;
		Int64 mouseData;
		Int64 flags;
		Int64 time;
		IntPtr dwExtraInfo;
	}
	#endregion

	#region Class MouseLLEventArgs
	public class MouseLLEventArgs : MouseEventArgs {
		public IntPtr Handle;			// Win32 handle of the window
		public string ClassName;		// Window class name
		public string Title;			// Window title
		
		public MouseLLEventArgs(MouseButtons button, Int32 clicks, Int32 x, Int32 y, Int32 delta) 
		: base(button, clicks, x, y, delta) {}
	}
	#endregion


	#region Class MouseLLHook
	public class MouseLLHook : Hook {
		// ************************************************************************
		// Event delegate
		public delegate void MouseLLEventHandler(object sender, MouseLLEventArgs e);
		// ************************************************************************
		
		// ************************************************************************
		// Events 
		public event MouseLLEventHandler MouseClicked;
		// ************************************************************************


		// ************************************************************************
		// Internal properties
		protected IntPtr m_hwnd = IntPtr.Zero;
		protected MouseButtons m_button = MouseButtons.None;
		protected int m_clicks = 0;
		protected int m_delta = 0;
		protected int m_x = 0;
		protected int m_y = 0;
		protected string m_class = string.Empty;
		protected string m_title = string.Empty;
		// ************************************************************************


		// ************************************************************************
		// Class constructor(s)
		public MouseLLHook() : base(HookType.WH_MOUSE_LL) {
			this.HookInvoked += new HookEventHandler(MouseLLHookInvoked);
		}
		public MouseLLHook(HookProc func) : base(HookType.WH_MOUSE_LL, func) {
			this.HookInvoked += new HookEventHandler(MouseLLHookInvoked);
		}		
		// ************************************************************************


		// ************************************************************************
		// Handles the hook event
		private void MouseLLHookInvoked(object sender, HookEventArgs e) {
			MouseLLHookAction code = (MouseLLHookAction) e.HookCode; 
			IntPtr wParam = e.wParam; 
			IntPtr lParam = e.lParam;

			// Handle hook events (only a few of available actions)
			switch (code) {
				case MouseLLHookAction.WM_RBUTTONUP:
					HandleLButtonUpEvent(wParam, lParam);
					break;
			}
			
			return;
		}
		// ************************************************************************

        // ************************************************************************
		// Handle the ACTIVATE hook event
		private void HandleLButtonUpEvent(IntPtr wParam, IntPtr lParam) {
			// Cache some information
			UpdateWindowData(wParam);

			// raise event
			OnMouseClicked(wParam, lParam);
		}
		// ************************************************************************

		// ************************************************************************
		// Read and store some information about the window
		private void UpdateWindowData(IntPtr wParam) {
			// Cache the window handle
			m_hwnd = wParam;

			// Cache the window's class name
			StringBuilder sb1 = new StringBuilder();
			sb1.Capacity = 40;
			GetClassName(m_hwnd, sb1, 40);
			m_class = sb1.ToString();

			// Cache the window's title bar
			StringBuilder sb2 = new StringBuilder();
			sb2.Capacity = 256;
			GetWindowText(m_hwnd, sb2, 256);
			m_title = sb2.ToString();

			// Cache the dialog flag
			//m_isDialog = (m_class == "#32770"); 
		}
		// ************************************************************************


		// ************************************************************************
		// Helper functions that fire events by executing user code
		protected virtual void OnMouseClicked(IntPtr wParam) {
			if (MouseClicked != null) {
				MouseLLEventArgs e = new MouseLLEventArgs();
				PrepareEventData(e);
				MouseClicked(this, e);
			}
		}
		// ************************************************************************


		// ************************************************************************
		// Prepare the event data structure
		private void PrepareEventData(MouseLLEventArgs e) {
			e.Handle = m_hwnd;
			e.Title = m_title;
			e.ClassName = m_class;
			//e.IsDialogWindow = m_isDialog;
		}
		// ************************************************************************


		#region Win32 Imports
		// ************************************************************************
		// Win32: GetClassName
		[DllImport("user32.dll")]
		protected static extern int GetClassName(IntPtr hwnd,
			StringBuilder lpClassName, int nMaxCount);
		// ************************************************************************

		// ************************************************************************
		// Win32: GetWindowText
		[DllImport("user32.dll")]
		protected static extern int GetWindowText(IntPtr hwnd,
			StringBuilder lpString, int nMaxCount);
		// ************************************************************************
		#endregion
	}
	#endregion
}
